home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / python-support / gnome-orca / orca / mag.py < prev    next >
Encoding:
Python Source  |  2009-04-13  |  59.8 KB  |  1,780 lines

  1. # Orca
  2. #
  3. # Copyright 2005-2008 Sun Microsystems Inc.
  4. #
  5. # This library is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU Library General Public
  7. # License as published by the Free Software Foundation; either
  8. # version 2 of the License, or (at your option) any later version.
  9. #
  10. # This library is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13. # Library General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU Library General Public
  16. # License along with this library; if not, write to the
  17. # Free Software Foundation, Inc., Franklin Street, Fifth Floor,
  18. # Boston MA  02110-1301 USA.
  19.  
  20. """Manages the magnifier for orca.  [[[TODO: WDW - this is very very
  21. early in development.  One might even say it is pre-prototype.]]]"""
  22.  
  23. __id__        = "$Id: mag.py 4607 2009-02-26 18:12:30Z wwalker $"
  24. __version__   = "$Revision: 4607 $"
  25. __date__      = "$Date: 2009-02-26 13:12:30 -0500 (Thu, 26 Feb 2009) $"
  26. __copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
  27. __license__   = "LGPL"
  28.  
  29. import bonobo
  30. try:
  31.     # This can fail due to gtk not being available.  We want to
  32.     # be able to recover from that if possible.  The main driver
  33.     # for this is to allow "orca --text-setup" to work even if
  34.     # the desktop is not running.
  35.     #
  36.     import gtk
  37. except:
  38.     pass
  39. import time
  40.  
  41. import pyatspi
  42. import debug
  43. import eventsynthesizer
  44. import settings
  45. import speech
  46. import orca_state
  47.  
  48. from orca_i18n import _  # for gettext support
  49.  
  50. _magnifierAvailable = False
  51.  
  52. try:
  53.     import ORBit
  54.     ORBit.load_typelib('GNOME_Magnifier')
  55.     import GNOME.Magnifier
  56.     _magnifierAvailable = True
  57. except:
  58.     pass
  59.  
  60. # If True, this module has been initialized.
  61. #
  62. _initialized = False
  63.  
  64. # The Magnifier and its property bag
  65. #
  66. _magnifier = None
  67. _magnifierPBag = None
  68.  
  69. # The width and height, in unzoomed system coordinates of the rectangle that,
  70. # when magnified, will fill the viewport of the magnifier - this needs to be
  71. # sync'd with the magnification factors of the zoom area.
  72. #
  73. _roiWidth = 0
  74. _roiHeight = 0
  75.  
  76. # Minimum/maximum values for the center of the ROI
  77. # in source screen coordinates.
  78. #
  79. _minROIX = 0
  80. _maxROIX = 0
  81. _minROIY = 0
  82. _maxROIY = 0
  83.  
  84. # The current region of interest.
  85. #
  86. # A GNOME.Magnifier.RectBounds object which consists of x1, y1, x2, y2 values
  87. # that are in source screen coordinates.
  88. #
  89. _roi = None
  90.  
  91. # The area of the source display that is not covered by the magnifier.
  92. #
  93. # A GNOME.Magnifier.RectBounds object which consists of x1, y1, x2, y2 values
  94. # that are in source screen coordinates.  If the COMPOSITE support is enabled
  95. # in gnome-mag, then this typically becomes the entire source screen.  If it
  96. # it not enabled, however, and the target and source displays are the same,
  97. # this ends up becoming the portion of the screen that is not covered by the
  98. # magnifier.
  99. #
  100. _sourceDisplayBounds = None
  101.  
  102. # The area on the target display where we are placing the magnifier.
  103. #
  104. # A GNOME.Magnifier.RectBounds object which consists of x1, y1, x2, y2 values
  105. # that are in target screen coordinates.
  106. #
  107. _targetDisplayBounds = None
  108.  
  109. # The ZoomRegion we care about and its property bag.  We only use one
  110. # ZoomRegion and we make it occupy the whole magnifier.
  111. #
  112. _zoomer = None
  113. _zoomerPBag = None
  114.  
  115. # The time of the last mouse event.
  116. #
  117. _lastMouseEventTime = time.time()
  118.  
  119. # Whether or not the last mouse event was the result of our routing the
  120. # pointer.
  121. #
  122. _lastMouseEventWasRoute = False
  123.  
  124. # If True, we're using gnome-mag >= 0.13.1 that allows us to control
  125. # where to draw the cursor and crosswires.
  126. #
  127. _pollMouseDisabled = False
  128.  
  129. # Whether or not composite is being used.
  130. #
  131. _fullScreenCapable = True
  132.  
  133. # Whether or not we're in the process of making "live update" changes
  134. # to the location of the magnifier.
  135. #
  136. _liveUpdatingMagnifier = False
  137.  
  138. # The original source display bounds.
  139. #
  140. _originalSourceDisplayBounds = None
  141.  
  142. # The current modes of tracking, for use with "live update" changes.
  143. #
  144. _mouseTracking = None
  145. _controlTracking = None
  146. _textTracking = None
  147. _edgeMargin = None
  148. _pointerFollowsZoomer = None
  149. _pointerFollowsFocus = None
  150.  
  151. def __setROI(rect):
  152.     """Sets the region of interest.
  153.  
  154.     Arguments:
  155.     - rect: A GNOME.Magnifier.RectBounds object.
  156.     """
  157.  
  158.     global _roi
  159.  
  160.     debug.println(debug.LEVEL_ALL, "mag.py:__setROI: (%d, %d), (%d, %d)" \
  161.                   % (rect.x1, rect.y1, rect.x2, rect.y2))
  162.  
  163.     _roi = rect
  164.  
  165.     _zoomer.setROI(_roi)
  166.     _zoomer.markDirty(_roi)  # [[[TODO: WDW - for some reason, this seems
  167.                              # necessary.]]]
  168. def __setROICenter(x, y):
  169.     """Centers the region of interest around the given point.
  170.  
  171.     Arguments:
  172.     - x: integer in unzoomed system coordinates representing x component
  173.     - y: integer in unzoomed system coordinates representing y component
  174.     """
  175.  
  176.     if not _initialized:
  177.         return
  178.  
  179.     if x < _minROIX:
  180.         x = _minROIX
  181.     elif x > _maxROIX:
  182.         x = _maxROIX
  183.  
  184.     if y < _minROIY:
  185.         y = _minROIY
  186.     elif y > _maxROIY:
  187.         y = _maxROIY
  188.  
  189.     x1 = x - (_roiWidth / 2)
  190.     y1 = y - (_roiHeight / 2)
  191.  
  192.     x2 = x1 + _roiWidth
  193.     y2 = y1 + _roiHeight
  194.  
  195.     __setROI(GNOME.Magnifier.RectBounds(x1, y1, x2, y2))
  196.  
  197. def __setROIPush(x, y):
  198.     """Nudges the ROI if the pointer bumps into the edge of it.  The point
  199.     given is assumed to be the point where the mouse pointer is.
  200.  
  201.     Arguments:
  202.     - x: integer in unzoomed system coordinates representing x component
  203.     - y: integer in unzoomed system coordinates representing y component
  204.     """
  205.  
  206.     #needNewROI = False
  207.     newROI = GNOME.Magnifier.RectBounds(_roi.x1, _roi.y1, _roi.x2, _roi.y2)
  208.     if x < _roi.x1:
  209.         #needNewROI = True
  210.         newROI.x1 = x
  211.         newROI.x2 = x + _roiWidth
  212.     elif x > _roi.x2:
  213.         #needNewROI = True
  214.         newROI.x2 = x
  215.         newROI.x1 = x - _roiWidth
  216.     if y < _roi.y1:
  217.         #needNewROI = True
  218.         newROI.y1 = y
  219.         newROI.y2 = y + _roiHeight
  220.     elif y > _roi.y2:
  221.         #needNewROI = True
  222.         newROI.y2 = y
  223.         newROI.y1 = y - _roiHeight
  224.  
  225.     # Well...we'll always update the ROI so the new gnome-mag API
  226.     # will redraw the crosswires for us.
  227.     #
  228.     #if needNewROI:
  229.     if True:
  230.         __setROI(newROI)
  231.  
  232. def __setROICursorPush(x, y, width, height, edgeMargin = 0):
  233.     """Nudges the ROI if the caret or control is not visible.
  234.  
  235.     Arguments:
  236.     - x: integer in unzoomed system coordinates representing x component
  237.     - y: integer in unzoomed system coordinates representing y component
  238.     - width: integer in unzoomed system coordinates representing the width
  239.     - height: integer in unzoomed system coordinates representing the height
  240.     - edgeMargin: a percentage representing how close to the edge we can get
  241.                   before we need to push
  242.     """
  243.  
  244.     # The edge margin should not exceed 50%. (50% is a centered alignment).
  245.     #
  246.     edgeMargin = min(edgeMargin, 50)/100.00
  247.     edgeMarginX = edgeMargin * _sourceDisplayBounds.x2/settings.magZoomFactor
  248.     edgeMarginY = edgeMargin * _sourceDisplayBounds.y2/settings.magZoomFactor
  249.  
  250.     # Determine if the accessible is partially to the left, right,
  251.     # above, or below the current region of interest (ROI).
  252.     #
  253.     leftOfROI = (x - edgeMarginX) <= _roi.x1
  254.     rightOfROI = (x + width + edgeMarginX) >= _roi.x2
  255.     aboveROI = (y - edgeMarginY)  <= _roi.y1
  256.     belowROI = (y + height + edgeMarginY) >= _roi.y2
  257.  
  258.     # If it is already completely showing, do nothing.
  259.     #
  260.     visibleX = not(leftOfROI or rightOfROI)
  261.     visibleY = not(aboveROI or belowROI)
  262.     if visibleX and visibleY:
  263.         _zoomer.markDirty(_roi)
  264.  
  265.     # The algorithm is devised to move the ROI as little as possible, yet
  266.     # favor the top left side of the object [[[TODO: WDW - the left/right
  267.     # top/bottom favoring should probably depend upon the locale.  Also,
  268.     # I had the notion of including a floating point snap factor between
  269.     # 0.0 and 1.0 that would determine how to position the object in the
  270.     # window relative to the ROI edges.  A snap factor of -1 would mean to
  271.     # snap to the closest edge.  A snap factor of 0.0 would snap to the
  272.     # left most or top most edge, a snap factor of 1.0 would snap to the
  273.     # right most or bottom most edge.  Any number in between would divide
  274.     # the two.]]]
  275.     #
  276.     x1 = _roi.x1
  277.     x2 = _roi.x2
  278.     y1 = _roi.y1
  279.     y2 = _roi.y2
  280.  
  281.     if leftOfROI:
  282.         x1 = max(_sourceDisplayBounds.x1, x - edgeMarginX)
  283.         x2 = x1 + _roiWidth
  284.     elif rightOfROI:
  285.         x = min(_sourceDisplayBounds.x2, x + edgeMarginX)
  286.         if width > _roiWidth:
  287.             x1 = x
  288.             x2 = x1 + _roiWidth
  289.         else:
  290.             x2 = x + width
  291.             x1 = x2 - _roiWidth
  292.  
  293.     if aboveROI:
  294.         y1 = max(_sourceDisplayBounds.y1, y - edgeMarginY)
  295.         y2 = y1 + _roiHeight
  296.     elif belowROI:
  297.         y = min(_sourceDisplayBounds.y2, y + edgeMarginY)
  298.         if height > _roiHeight:
  299.             y1 = y
  300.             y2 = y1 + _roiHeight
  301.         else:
  302.             y2 = y + height
  303.             y1 = y2 - _roiHeight
  304.  
  305.     __setROI(GNOME.Magnifier.RectBounds(x1, y1, x2, y2))
  306.  
  307. def __setROIProportional(x, y):
  308.     """Positions the ROI proportionally to where the pointer is on the screen.
  309.  
  310.     Arguments:
  311.     - x: integer in unzoomed system coordinates representing x component
  312.     - y: integer in unzoomed system coordinates representing y component
  313.     """
  314.  
  315.     if not _initialized:
  316.         return
  317.  
  318.     if not _sourceDisplayBounds:
  319.         __setROICenter(x, y)
  320.     else:
  321.         halfScreenWidth  = (_sourceDisplayBounds.x2 \
  322.                             - _sourceDisplayBounds.x1) / 2.0
  323.         halfScreenHeight = (_sourceDisplayBounds.y2 \
  324.                             - _sourceDisplayBounds.y1) / 2.0
  325.  
  326.         proportionX = (halfScreenWidth  - x) / halfScreenWidth
  327.         proportionY = (halfScreenHeight - y) / halfScreenHeight
  328.  
  329.         centerX = x + int(proportionX * _roiWidth  / 2.0)
  330.         centerY = y + int(proportionY * _roiHeight / 2.0)
  331.  
  332.         __setROICenter(centerX, centerY)
  333.  
  334. # Used for tracking the pointer.
  335. #
  336. def __onMouseEvent(e):
  337.     """
  338.     Arguments:
  339.     - e: at-spi event from the at-api registry
  340.     """
  341.  
  342.     global _lastMouseEventTime
  343.     global _lastMouseEventWasRoute
  344.  
  345.     isNewMouseMovement = (time.time() - _lastMouseEventTime > 1)
  346.     _lastMouseEventTime = time.time()
  347.  
  348.     [x, y] = [e.detail1, e.detail2]
  349.  
  350.     if _pointerFollowsZoomer and isNewMouseMovement \
  351.        and not _lastMouseEventWasRoute:
  352.         mouseIsVisible = (_roi.x1 < x < _roi.x2) and (_roi.y1 < y < _roi.y2)
  353.         if not mouseIsVisible and orca_state.locusOfFocus:
  354.             if _mouseTracking == settings.MAG_TRACKING_MODE_CENTERED:
  355.                 x = (_roi.x1 + _roi.x2) / 2
  356.                 y = (_roi.y1 + _roi.y2) / 2
  357.             elif _mouseTracking != settings.MAG_TRACKING_MODE_NONE:
  358.                 try:
  359.                     extents = \
  360.                         orca_state.locusOfFocus.queryComponent().getExtents(0)
  361.                 except:
  362.                     extents = None
  363.                 if extents:
  364.                     x = extents.x
  365.                     y = extents.y + extents.height - 1
  366.  
  367.             eventsynthesizer.generateMouseEvent(x, y, "abs")
  368.             _lastMouseEventWasRoute = True
  369.  
  370.     # If True, we're using gnome-mag >= 0.13.1 that allows us to
  371.     # control where to draw the cursor and crosswires.
  372.     #
  373.     if _pollMouseDisabled:
  374.         _zoomer.setPointerPos(x, y)
  375.  
  376.     if _lastMouseEventWasRoute:
  377.         # If we just moved the mouse pointer to the menu item or control
  378.         # with focus, we don't want to do anything.
  379.         #
  380.         _lastMouseEventWasRoute = False
  381.         _zoomer.markDirty(_roi)
  382.         return
  383.  
  384.     if _mouseTracking == settings.MAG_TRACKING_MODE_PUSH:
  385.         __setROIPush(x, y)
  386.     elif _mouseTracking == settings.MAG_TRACKING_MODE_PROPORTIONAL:
  387.         __setROIProportional(x, y)
  388.     elif _mouseTracking == settings.MAG_TRACKING_MODE_CENTERED:
  389.         __setROICenter(x, y)
  390.  
  391. def __getValueText(slot, value):
  392.     valueText = ""
  393.     if slot == "cursor-hotspot":
  394.         valueText = "(%d, %d)" % (value.x, value.y)
  395.     elif slot == "source-display-bounds":
  396.         valueText = "(%d, %d),(%d, %d)" \
  397.                     % (value.x1, value.y1, value.x2, value.y2)
  398.     elif slot == "target-display-bounds":
  399.         valueText = "(%d, %d),(%d, %d)" \
  400.                     % (value.x1, value.y1, value.x2, value.y2)
  401.     elif slot == "viewport":
  402.         valueText = "(%d, %d),(%d, %d)" \
  403.                     % (value.x1, value.y1, value.x2, value.y2)
  404.     return valueText
  405.  
  406. def __dumpPropertyBag(obj):
  407.     pbag = obj.getProperties()
  408.     slots = pbag.getKeys("")
  409.     print "  Available slots: ", pbag.getKeys("")
  410.     for slot in slots:
  411.         # These crash the magnifier since it doesn't know how to marshall
  412.         # them to us.
  413.         #
  414.         if slot in ["cursor-set", "smoothing-type"]:
  415.             continue
  416.         print "    About '%s':" % slot
  417.         print "    Doc Title:", pbag.getDocTitle(slot)
  418.         print "    Type:", pbag.getType(slot)
  419.         value = pbag.getDefault(slot).value()
  420.         print "    Default value:", value, __getValueText(slot, value)
  421.         value = pbag.getValue(slot).value()
  422.         print "    Current value:", value, __getValueText(slot, value)
  423.         print
  424.  
  425. def __setupMagnifier(position, left=None, top=None, right=None, bottom=None,
  426.                      restore=None):
  427.     """Creates the magnifier in the position specified.
  428.  
  429.     Arguments:
  430.     - position: the position/type of zoomer (full, left half, etc.)
  431.     - left:     the left edge of the zoomer (only applicable for custom)
  432.     - top:      the top edge of the zoomer (only applicable for custom)
  433.     - right:    the right edge of the zoomer (only applicable for custom)
  434.     - bottom:   the top edge of the zoomer (only applicable for custom)
  435.     - restore:  a dictionary of all of the settings which should be restored
  436.     """
  437.  
  438.     global _fullScreenCapable
  439.     global _magnifierPBag
  440.     global _originalSourceDisplayBounds
  441.  
  442.     _magnifier.clearAllZoomRegions()
  443.  
  444.     if not restore:
  445.         restore = {}
  446.  
  447.     # Define where the magnifier will live.
  448.     #
  449.     try:
  450.         _magnifier.TargetDisplay = settings.magTargetDisplay
  451.     except:
  452.         pass
  453.  
  454.     # Define what will be magnified.
  455.     #
  456.     try:
  457.         _magnifier.SourceDisplay = settings.magSourceDisplay
  458.     except:
  459.         pass
  460.  
  461.     # Find out if we're using composite.
  462.     #
  463.     try:
  464.         _fullScreenCapable = _magnifier.fullScreenCapable()
  465.     except:
  466.         debug.printException(debug.LEVEL_WARNING)
  467.  
  468.     # If we are running in full screen mode, try to hide the original cursor
  469.     # (assuming the user wants to). See bug #533095 for more details.
  470.     # Depends upon new functionality in gnome-mag, so just catch the 
  471.     # exception if this functionality isn't there.
  472.     #
  473.     hideCursor = restore.get('magHideCursor', settings.magHideCursor)
  474.     if hideCursor \
  475.        and _fullScreenCapable \
  476.        and _magnifier.SourceDisplay == _magnifier.TargetDisplay \
  477.        and position == settings.MAG_ZOOMER_TYPE_FULL_SCREEN:
  478.         hideSystemPointer(True)
  479.     else:
  480.         hideSystemPointer(False)
  481.  
  482.     _magnifierPBag = _magnifier.getProperties()
  483.     sdb = _magnifierPBag.getValue("source-display-bounds").value()
  484.     if not _originalSourceDisplayBounds:
  485.         _originalSourceDisplayBounds = sdb
  486.     elif _liveUpdatingMagnifier:
  487.         sdb = _originalSourceDisplayBounds
  488.  
  489.     # Find out where the user wants to place the target display.
  490.     #
  491.     if _fullScreenCapable and \
  492.        position == settings.MAG_ZOOMER_TYPE_FULL_SCREEN:
  493.         prefLeft = 0
  494.         prefTop = 0
  495.         prefRight = sdb.x2
  496.         prefBottom = sdb.y2
  497.     elif position == settings.MAG_ZOOMER_TYPE_TOP_HALF:
  498.         prefLeft = 0
  499.         prefTop = 0
  500.         prefRight = sdb.x2
  501.         prefBottom = sdb.y2 / 2
  502.     elif position == settings.MAG_ZOOMER_TYPE_BOTTOM_HALF:
  503.         prefLeft = 0
  504.         prefTop = sdb.y2 / 2
  505.         prefRight = sdb.x2
  506.         prefBottom = sdb.y2
  507.     elif position == settings.MAG_ZOOMER_TYPE_LEFT_HALF:
  508.         prefLeft = 0
  509.         prefTop = 0
  510.         prefRight = sdb.x2 / 2
  511.         prefBottom = sdb.y2
  512.     elif position == settings.MAG_ZOOMER_TYPE_RIGHT_HALF:
  513.         prefLeft = sdb.x2 / 2
  514.         prefTop = 0
  515.         prefRight = sdb.x2
  516.         prefBottom = sdb.y2
  517.     else:
  518.         prefLeft   = left or settings.magZoomerLeft
  519.         prefTop    = top or settings.magZoomerTop
  520.         prefRight  = right or settings.magZoomerRight
  521.         prefBottom = bottom or settings.magZoomerBottom
  522.     orca_state.zoomerType = position
  523.     updateTarget = True
  524.  
  525.     # If we're not using composite, bad things will happen if we allow the
  526.     # target display to occupy more than 50% of the full screen.  Also, if
  527.     # it occupies the same space as something that should be magnified (e.g.
  528.     # the Applications menu), that item will not be magnified. Therefore,
  529.     # we're going to prefer the right half of the screen for the target
  530.     # display -- unless gnome-mag is already running and not in "full screen"
  531.     # mode.
  532.     #
  533.     if not _fullScreenCapable \
  534.        and _magnifier.SourceDisplay == _magnifier.TargetDisplay:
  535.         # At this point, the target display bounds have not been set. if they
  536.         # are all 0, then we know that gnome-mag isn't already running.
  537.         #
  538.         magAlreadyRunning = False
  539.         tdb = _magnifierPBag.getValue("target-display-bounds").value()
  540.         if tdb.x1 or tdb.x2 or tdb.y1 or tdb.y2:
  541.             magAlreadyRunning = True
  542.  
  543.         # At this point, because we haven't set the target display bounds,
  544.         # gnome-mag has not modified the source display bounds. Therefore,
  545.         # we can use the current source display bounds to get the
  546.         # dimensions of the full screen -- assuming gnome-mag has not
  547.         # already been launched with a split screen. Comparing the values
  548.         # of the source and target display bounds lets us know if gnome-mag
  549.         # has been started in "full screen" mode.
  550.         #
  551.         magFullScreen = magAlreadyRunning \
  552.                         and (tdb.x1 == sdb.x1) and (tdb.x2 == sdb.x2) \
  553.                         and (tdb.y1 == sdb.y1) and (tdb.y2 == sdb.y2)
  554.  
  555.         sourceArea = (sdb.x2 - sdb.x1) * (sdb.y2 - sdb.y1)
  556.         prefArea = (prefRight - prefLeft) * (prefBottom - prefTop)
  557.         if prefArea > sourceArea/2:
  558.             debug.println(
  559.                 debug.LEVEL_WARNING,
  560.                 "Composite is not being used. The preferred target area is\n"\
  561.                 "greater than 50% of the source area.  These settings can\n"\
  562.                 "render the contents of the screen inaccessible.")
  563.             if not magAlreadyRunning or magFullScreen:
  564.                 debug.println(
  565.                     debug.LEVEL_WARNING,
  566.                     "Setting the target display to the screen's right half.")
  567.                 prefRight  = sdb.x2
  568.                 prefLeft   = sdb.x2/2
  569.                 prefTop    = sdb.y1
  570.                 prefBottom = sdb.y2
  571.             elif sdb.y2 > 0:
  572.                 updateTarget = False
  573.                 debug.println(
  574.                     debug.LEVEL_WARNING,
  575.                     "Gnome-mag is already running.  Using those settings.")
  576.  
  577.     # Now, tell the magnifier where we want it to live on the target display.
  578.     # The coordinates are in screen coordinates of the target display.
  579.     # This will have the side effect of setting up other stuff for us, such
  580.     # as source-display-bouinds.
  581.     #
  582.     if updateTarget:
  583.         tdb = _magnifierPBag.getValue("target-display-bounds").value()
  584.         _magnifierPBag.setValue(
  585.             "target-display-bounds",
  586.             ORBit.CORBA.Any(
  587.                 ORBit.CORBA.TypeCode(
  588.                     tdb.__typecode__.repo_id),
  589.                 GNOME.Magnifier.RectBounds(prefLeft,
  590.                                            prefTop,
  591.                                            prefRight,
  592.                                            prefBottom)))
  593.  
  594.     bonobo.pbclient_set_string(_magnifierPBag, "cursor-set", "default")
  595.  
  596.     enableCursor = restore.get('enableMagCursor', settings.enableMagCursor)
  597.     explicitSize = restore.get('enableMagCursorExplicitSize',
  598.                                settings.enableMagCursorExplicitSize)
  599.     size = restore.get('magCursorSize', settings.magCursorSize)
  600.     setMagnifierCursor(enableCursor, explicitSize, size, False)
  601.  
  602.     value = restore.get('magCursorColor', settings.magCursorColor)
  603.     setMagnifierObjectColor("cursor-color", value, False)
  604.  
  605.     value = restore.get('magCrossHairColor', settings.magCrossHairColor)
  606.     setMagnifierObjectColor("crosswire-color", value, False)
  607.  
  608.     enableCrossHair = restore.get('enableMagCrossHair',
  609.                                   settings.enableMagCrossHair)
  610.     setMagnifierCrossHair(enableCrossHair, False)
  611.  
  612.     value = restore.get('enableMagCrossHairClip',
  613.                         settings.enableMagCrossHairClip)
  614.     setMagnifierCrossHairClip(value, False)
  615.  
  616.     orca_state.mouseEnhancementsEnabled = enableCursor or enableCrossHair
  617.  
  618. def __setupZoomer(restore=None):
  619.     """Creates a zoomer in the magnifier
  620.     Arguments:
  621.     - restore:  a dictionary of all of the settings which should be restored
  622.     """
  623.  
  624.     global _sourceDisplayBounds
  625.     global _targetDisplayBounds
  626.     global _zoomer
  627.     global _roiWidth
  628.     global _roiHeight
  629.     global _pollMouseDisabled
  630.     global _zoomerPBag
  631.     global _fullScreenCapable
  632.  
  633.     if not restore:
  634.         restore = {}
  635.  
  636.     _targetDisplayBounds = _magnifierPBag.getValue(
  637.         "target-display-bounds").value()
  638.  
  639.     debug.println(debug.LEVEL_ALL,
  640.                   "Magnifier target bounds preferences: (%d, %d), (%d, %d)" \
  641.                   % (settings.magZoomerLeft, settings.magZoomerTop, \
  642.                      settings.magZoomerRight, settings.magZoomerBottom))
  643.  
  644.     debug.println(debug.LEVEL_ALL,
  645.                   "Magnifier target bounds actual: (%d, %d), (%d, %d)" \
  646.                   % (_targetDisplayBounds.x1, _targetDisplayBounds.y1, \
  647.                      _targetDisplayBounds.x2, _targetDisplayBounds.y2))
  648.  
  649.     # If the COMPOSITE support is not enabled in gnome-mag, then the
  650.     # source-display-bounds will be adjusted to accomodate portion of the
  651.     # display not being covered by the magnifier (assuming there is only
  652.     # one display).  Otherwise, the source-display-bounds will be the
  653.     # entire source screen.
  654.     #
  655.     _sourceDisplayBounds = _magnifierPBag.getValue(
  656.         "source-display-bounds").value()
  657.  
  658.     debug.println(debug.LEVEL_ALL,
  659.                   "Magnifier source bounds actual: (%d, %d), (%d, %d)" \
  660.                   % (_sourceDisplayBounds.x1, _sourceDisplayBounds.y1, \
  661.                      _sourceDisplayBounds.x2, _sourceDisplayBounds.y2))
  662.  
  663.     # If there is nothing we can possibly magnify, switch to the right half.
  664.     #
  665.     if ((_sourceDisplayBounds.x2 - _sourceDisplayBounds.x1) == 0) \
  666.         or ((_sourceDisplayBounds.y2 - _sourceDisplayBounds.y1) == 0):
  667.         debug.println(
  668.             debug.LEVEL_SEVERE,
  669.             "There is nothing to magnify.  This is usually caused\n" \
  670.             "by a preferences setting that tries to take up the\n" \
  671.             "full screen for magnification, but the underlying\n"
  672.             "system does not support full screen magnification.\n"
  673.             "The causes of that are generally that COMPOSITE\n" \
  674.             "support has not been enabled in gnome-mag or the\n" \
  675.             "X Window System Server.  As a result of this issue,\n" \
  676.             "defaulting to the right half of the screen.\n")
  677.         _fullScreenCapable = False
  678.         __setupMagnifier(settings.MAG_ZOOMER_TYPE_CUSTOM,
  679.                          _targetDisplayBounds.x1/2,
  680.                          _targetDisplayBounds.y1,
  681.                          _targetDisplayBounds.x2,
  682.                          _targetDisplayBounds.y2)
  683.         _sourceDisplayBounds = _magnifierPBag.getValue(
  684.             "source-display-bounds").value()
  685.         _targetDisplayBounds = _magnifierPBag.getValue(
  686.             "target-display-bounds").value()
  687.  
  688.     # Now, we create a zoom region to occupy the whole magnifier (i.e.,
  689.     # the viewport is in target region coordinates and we make the
  690.     # viewport be the whole target region).  Note, since we're starting
  691.     # at (0, 0), the viewportWidth and viewportHeight are the same as
  692.     # the x2, y2 values for a rectangular region.
  693.     #
  694.     viewportWidth = _targetDisplayBounds.x2 - _targetDisplayBounds.x1
  695.     viewportHeight = _targetDisplayBounds.y2 - _targetDisplayBounds.y1
  696.  
  697.     debug.println(debug.LEVEL_ALL,
  698.                   "Magnifier zoomer viewport desired: (0, 0), (%d, %d)" \
  699.                   % (viewportWidth, viewportHeight))
  700.  
  701.     # Now, let's see what the ROI looks like.
  702.     #
  703.     debug.println(debug.LEVEL_ALL,
  704.                   "Magnifier source width: %d (viewport can show %d)" \
  705.                   % (_sourceDisplayBounds.x2 - _sourceDisplayBounds.x1,
  706.                    viewportWidth / settings.magZoomFactor))
  707.     debug.println(debug.LEVEL_ALL,
  708.                   "Magnifier source height: %d (viewport can show %d)" \
  709.                   % (_sourceDisplayBounds.y2 - _sourceDisplayBounds.y1,
  710.                    viewportHeight / settings.magZoomFactor))
  711.  
  712.     # Adjust the ROI in the event the source window is too small for the
  713.     # target window.  This usually happens when someone expects COMPOSITE
  714.     # to be enabled, but it isn't.  As a result, they usually have a very
  715.     # big grey magnifier on their screen.
  716.     #
  717.     _roiWidth  = min(_sourceDisplayBounds.x2 - _sourceDisplayBounds.x1,
  718.                      viewportWidth / settings.magZoomFactor)
  719.     _roiHeight = min(_sourceDisplayBounds.y2 - _sourceDisplayBounds.y1,
  720.                      viewportHeight / settings.magZoomFactor)
  721.  
  722.     debug.println(debug.LEVEL_ALL,
  723.                   "Magnifier zoomer ROI size desired: width=%d, height=%d)" \
  724.                   % (_roiWidth, _roiHeight))
  725.  
  726.     # Create the zoomer with a magnification factor, an initial ROI, and
  727.     # where in magnifier we want it to be (we want it to be in the whole
  728.     # magnifier). Initially set the viewport so that it does not appear.
  729.     # After we set all of the color properties, reset the viewport to
  730.     # the correct position.  This will prevent the user from seeing the
  731.     # individual property changes (e.g. brightness, contrast) upon load.
  732.     #
  733.     _zoomer = _magnifier.createZoomRegion(
  734.         settings.magZoomFactor, settings.magZoomFactor,
  735.         GNOME.Magnifier.RectBounds(0, 0, _roiWidth, _roiHeight),
  736.         GNOME.Magnifier.RectBounds(0, 0, 1, 1))
  737.  
  738.     _zoomerPBag = _zoomer.getProperties()
  739.     bonobo.pbclient_set_boolean(_zoomerPBag, "is-managed", True)
  740.  
  741.     value = restore.get('magZoomFactor', settings.magZoomFactor)
  742.     setZoomerMagFactor(value, value, False)
  743.  
  744.     value = restore.get('enableMagZoomerColorInversion',
  745.                         settings.enableMagZoomerColorInversion)
  746.     setZoomerColorInversion(value, False)
  747.  
  748.     brightness = restore.get('magBrightnessLevel', settings.magBrightnessLevel)
  749.     r = brightness + \
  750.         restore.get('magBrightnessLevelRed',
  751.                     settings.magBrightnessLevelRed)
  752.     g = brightness + \
  753.         restore.get('magBrightnessLevelGreen',
  754.                     settings.magBrightnessLevelGreen)
  755.     b = brightness + \
  756.         restore.get('magBrightnessLevelBlue',
  757.                     settings.magBrightnessLevelBlue)
  758.     setZoomerBrightness(r, g, b, False)
  759.  
  760.     contrast = restore.get('magContrastLevel', settings.magContrastLevel)
  761.     r = contrast + \
  762.         restore.get('magContrastLevelRed',
  763.                     settings.magContrastLevelRed)
  764.     g = contrast + \
  765.         restore.get('magContrastLevelGreen',
  766.                     settings.magContrastLevelGreen)
  767.     b = contrast + \
  768.         restore.get('magContrastLevelBlue',
  769.                     settings.magContrastLevelBlue)
  770.     setZoomerContrast(r, g, b, False)
  771.  
  772.     value = restore.get('magColorFilteringMode',
  773.                         settings.magColorFilteringMode)
  774.     setZoomerColorFilter(value, False)
  775.  
  776.     value = restore.get('magZoomerType', settings.magZoomerType)
  777.     if value == settings.MAG_ZOOMER_TYPE_FULL_SCREEN:
  778.         size = 0
  779.     else:
  780.         size = restore.get('magZoomerBorderSize', settings.magZoomerBorderSize)
  781.     color = restore.get('magZoomerBorderColor', settings.magZoomerBorderColor)
  782.     setZoomerObjectSize("border-size", size, False)
  783.     setZoomerObjectColor("border-color", color, False)
  784.  
  785.     value = restore.get('magSmoothingMode', settings.magSmoothingMode)
  786.     setZoomerSmoothingType(value, False)
  787.  
  788.     # Now it's safe to display the viewport.
  789.     #
  790.     bounds = GNOME.Magnifier.RectBounds(0, 0, viewportWidth, viewportHeight)
  791.     _zoomer.moveResize(bounds)
  792.  
  793.     # Try to use gnome-mag >= 0.13.1 to allow us to control where to
  794.     # draw the cursor and crosswires.
  795.     #
  796.     try:
  797.         bonobo.pbclient_set_boolean(_zoomerPBag, "poll-mouse", False)
  798.         _pollMouseDisabled = True
  799.     except:
  800.         _pollMouseDisabled = False
  801.  
  802.     __updateROIDimensions()
  803.     _magnifier.addZoomRegion(_zoomer)
  804.  
  805. def __updateROIDimensions():
  806.     """Updates the ROI width, height, and maximum and minimum values.
  807.     """
  808.  
  809.     global _roiWidth
  810.     global _roiHeight
  811.     global _minROIX
  812.     global _minROIY
  813.     global _maxROIX
  814.     global _maxROIY
  815.  
  816.     viewport = _zoomerPBag.getValue("viewport").value()
  817.  
  818.     debug.println(debug.LEVEL_ALL,
  819.                   "Magnifier viewport actual: (%d, %d), (%d, %d)" \
  820.                   % (viewport.x1, viewport.y1, viewport.x2, viewport.y2))
  821.  
  822.     magx = _zoomerPBag.getValue("mag-factor-x").value()
  823.     magy = _zoomerPBag.getValue("mag-factor-y").value()
  824.  
  825.     _roiWidth  = min(_sourceDisplayBounds.x2 - _sourceDisplayBounds.x1,
  826.                      (viewport.x2 - viewport.x1) / magx)
  827.     _roiHeight = min(_sourceDisplayBounds.y2 - _sourceDisplayBounds.y1,
  828.                      (viewport.y2 - viewport.y1) / magy)
  829.  
  830.     debug.println(debug.LEVEL_ALL,
  831.                   "Magnifier zoomer ROI size actual: width=%d, height=%d)" \
  832.                   % (_roiWidth, _roiHeight))
  833.  
  834.     _minROIX = _sourceDisplayBounds.x1 + (_roiWidth / 2)
  835.     _minROIY = _sourceDisplayBounds.y1 + (_roiHeight / 2)
  836.  
  837.     _maxROIX = _sourceDisplayBounds.x2 - (_roiWidth / 2)
  838.     _maxROIY = _sourceDisplayBounds.y2 - (_roiHeight / 2)
  839.  
  840.     debug.println(debug.LEVEL_ALL,
  841.                   "Magnifier ROI min/max center: (%d, %d), (%d, %d)" \
  842.                   % (_minROIX, _minROIY, _maxROIX, _maxROIY))
  843.  
  844. def applySettings():
  845.     """Looks at the user settings and applies them to the magnifier."""
  846.  
  847.     global _mouseTracking
  848.     global _controlTracking
  849.     global _textTracking
  850.     global _edgeMargin
  851.     global _pointerFollowsZoomer
  852.     global _pointerFollowsFocus
  853.  
  854.     __setupMagnifier(settings.magZoomerType)
  855.     __setupZoomer()
  856.   
  857.     _mouseTracking = settings.magMouseTrackingMode
  858.     _controlTracking = settings.magControlTrackingMode
  859.     _textTracking = settings.magTextTrackingMode
  860.     _edgeMargin = settings.magEdgeMargin
  861.     _pointerFollowsZoomer = settings.magPointerFollowsZoomer
  862.     _pointerFollowsFocus = settings.magPointerFollowsFocus
  863.  
  864.     #print "MAGNIFIER PROPERTIES:", _magnifier
  865.     #__dumpPropertyBag(_magnifier)
  866.     #print "ZOOMER PROPERTIES:", _zoomer
  867.     #__dumpPropertyBag(_zoomer)
  868.  
  869. def magnifyAccessible(event, obj, extents=None):
  870.     """Sets the region of interest to the upper left of the given
  871.     accessible, if it implements the Component interface.  Otherwise,
  872.     does nothing.
  873.  
  874.     Arguments:
  875.     - event: the Event that caused this to be called
  876.     - obj: the accessible
  877.     """
  878.  
  879.     global _lastMouseEventWasRoute
  880.  
  881.     if not _initialized:
  882.         return
  883.  
  884.     # Avoid jerking the display around if the mouse is what ended up causing
  885.     # this event.  We guess this by seeing if this request has come in within
  886.     # a close period of time.  [[[TODO: WDW - this is a hack and really
  887.     # doesn't belong here.  Plus, the delta probably should be adjustable.]]]
  888.     #
  889.     currentTime = time.time()
  890.     if (currentTime - _lastMouseEventTime) < 0.2: # 200 milliseconds
  891.         return
  892.  
  893.     haveSomethingToMagnify = False
  894.  
  895.     if extents:
  896.         [x, y, width, height] = extents
  897.         haveSomethingToMagnify = True
  898.     elif event and event.type.startswith("object:text-caret-moved"):
  899.         try:
  900.             text = obj.queryText()
  901.             if text and (text.caretOffset >= 0):
  902.                 offset = text.caretOffset
  903.                 if offset == text.characterCount:
  904.                     offset -= 1
  905.                 [x, y, width, height] = \
  906.                     text.getCharacterExtents(offset, 0)
  907.                 haveSomethingToMagnify = (width + height > 0)
  908.                     
  909.         except:
  910.             haveSomethingToMagnify = False
  911.  
  912.         if haveSomethingToMagnify:
  913.             if _textTracking == settings.MAG_TRACKING_MODE_CENTERED:
  914.                 __setROICenter(x, y)
  915.             elif _textTracking == settings.MAG_TRACKING_MODE_PUSH:
  916.                 __setROICursorPush(x, y, width, height, _edgeMargin)
  917.             return
  918.  
  919.     if not haveSomethingToMagnify:
  920.         try:
  921.             extents = obj.queryComponent().getExtents(0)
  922.             [x, y, width, height] = \
  923.                 [extents.x, extents.y, extents.width, extents.height]
  924.             haveSomethingToMagnify = True
  925.         except:
  926.             haveSomethingToMagnify = False
  927.  
  928.     if haveSomethingToMagnify:
  929.         if _pointerFollowsFocus:
  930.             _lastMouseEventWasRoute = True
  931.             eventsynthesizer.generateMouseEvent(x, y + height - 1, "abs")
  932.  
  933.         if _controlTracking == settings.MAG_TRACKING_MODE_CENTERED:
  934.             centerX = x + width/2
  935.             centerY = y + height/2
  936.  
  937.             # Be sure that the upper-left corner of the object will still
  938.             # be visible on the screen.
  939.             #
  940.             if width > _roiWidth:
  941.                 centerX = x
  942.             if height > _roiHeight:
  943.                 centerY = y
  944.  
  945.             __setROICenter(centerX, centerY)
  946.         elif _controlTracking == settings.MAG_TRACKING_MODE_PUSH:
  947.             __setROICursorPush(x, y, width, height)
  948.  
  949. def init():
  950.     """Initializes the magnifier, bringing the magnifier up on the
  951.     display.
  952.  
  953.     Returns True if the initialization procedure was run or False if this
  954.     module has already been initialized.
  955.     """
  956.  
  957.     global _initialized
  958.     global _magnifier
  959.  
  960.     if not _magnifierAvailable:
  961.         return False
  962.  
  963.     if _initialized:
  964.         return False
  965.  
  966.     _magnifier = bonobo.get_object("OAFIID:GNOME_Magnifier_Magnifier:0.9",
  967.                                    "GNOME/Magnifier/Magnifier")
  968.  
  969.     try:
  970.         _initialized = True
  971.         applySettings()
  972.         pyatspi.Registry.registerEventListener(__onMouseEvent, "mouse:abs")
  973.  
  974.         # Zoom to the upper left corner of the display for now.
  975.         #
  976.         __setROICenter(0, 0)
  977.  
  978.         return True
  979.     except:
  980.         _initialized = False
  981.         _magnifier.dispose()
  982.         raise
  983.  
  984. def shutdown():
  985.     """Shuts down the magnifier module.
  986.     Returns True if the shutdown procedure was run or False if this
  987.     module has not been initialized.
  988.     """
  989.  
  990.     global _initialized
  991.     global _magnifier
  992.  
  993.     if not _magnifierAvailable:
  994.         return False
  995.  
  996.     if not _initialized:
  997.         return False
  998.  
  999.     pyatspi.Registry.deregisterEventListener(__onMouseEvent,"mouse:abs")
  1000.  
  1001.     # Someone might have killed the magnifier on us.  They shouldn't
  1002.     # have done so, but we need to be able to recover if they did.
  1003.     # See http://bugzilla.gnome.org/show_bug.cgi?id=375396.
  1004.     #
  1005.     try:
  1006.         hideSystemPointer(False)
  1007.         _magnifier.clearAllZoomRegions()
  1008.         _magnifier.dispose()
  1009.     except:
  1010.         debug.printException(debug.LEVEL_WARNING)
  1011.  
  1012.     _magnifier = None
  1013.  
  1014.     _initialized = False
  1015.  
  1016.     return True
  1017.  
  1018. ######################################################################
  1019. #                                                                    #
  1020. #              Convenience functions for "live" changes              #
  1021. #                                                                    #
  1022. ######################################################################
  1023.  
  1024. def setupMagnifier(position, left=None, top=None, right=None, bottom=None,
  1025.                    restore=None):
  1026.     """Creates the magnifier in the position specified.
  1027.  
  1028.     Arguments:
  1029.     - position: the position/type of zoomer (full, left half, etc.)
  1030.     - left:     the left edge of the zoomer (only applicable for custom)
  1031.     - top:      the top edge of the zoomer (only applicable for custom)
  1032.     - right:    the right edge of the zoomer (only applicable for custom)
  1033.     - bottom:   the top edge of the zoomer (only applicable for custom)
  1034.     - restore:  a dictionary of all of the settings that should be restored
  1035.     """
  1036.  
  1037.     if not _initialized:
  1038.         return
  1039.  
  1040.     global _liveUpdatingMagnifier
  1041.  
  1042.     _liveUpdatingMagnifier = True
  1043.     __setupMagnifier(position, left, top, right, bottom, restore)
  1044.     __setupZoomer(restore)
  1045.  
  1046. def setMagnifierCursor(enabled, customEnabled, size, updateScreen=True):
  1047.     """Sets the cursor.
  1048.  
  1049.     Arguments:
  1050.     - enabled:        Whether or not the cursor should be enabled
  1051.     - customEnabled:  Whether or not a custom size has been enabled
  1052.     - size:           The size it should be set to
  1053.     - updateScreen:   Whether or not to update the screen
  1054.     """
  1055.  
  1056.     if not _initialized:
  1057.         return
  1058.     try:
  1059.         mag = _zoomerPBag.getValue("mag-factor-x").value()
  1060.     except:
  1061.         mag = settings.magZoomFactor
  1062.  
  1063.     if enabled:
  1064.         scale = 1.0 * mag
  1065.     else:
  1066.         scale = 0.0
  1067.  
  1068.     if not (enabled and customEnabled):
  1069.         size = 0
  1070.  
  1071.     bonobo.pbclient_set_float(_magnifierPBag, "cursor-scale-factor", scale)
  1072.     bonobo.pbclient_set_long(_magnifierPBag, "cursor-size", size)
  1073.  
  1074.     if updateScreen:
  1075.         _zoomer.markDirty(_roi)
  1076.  
  1077. def setMagnifierCrossHair(enabled, updateScreen=True):
  1078.     """Sets the cross-hair.
  1079.  
  1080.     Arguments:
  1081.     - enabled: Whether or not the cross-hair should be enabled
  1082.     - updateScreen:  Whether or not to update the screen
  1083.     """
  1084.  
  1085.     if not _initialized:
  1086.         return
  1087.  
  1088.     size = 0
  1089.     if enabled:
  1090.         size = settings.magCrossHairSize
  1091.  
  1092.     bonobo.pbclient_set_long(_magnifierPBag, "crosswire-size", size)
  1093.  
  1094.     if updateScreen:
  1095.         _zoomer.markDirty(_roi)
  1096.  
  1097. def setMagnifierCrossHairClip(enabled, updateScreen=True):
  1098.     """Sets the cross-hair clip.
  1099.  
  1100.     Arguments:
  1101.     - enabled: Whether or not the cross-hair clip should be enabled
  1102.     - updateScreen:   Whether or not to update the screen
  1103.     """
  1104.  
  1105.     if not _initialized:
  1106.         return
  1107.  
  1108.     bonobo.pbclient_set_boolean(_magnifierPBag, "crosswire-clip", enabled)
  1109.  
  1110.     if updateScreen:
  1111.         _zoomer.markDirty(_roi)
  1112.  
  1113. def setZoomerColorInversion(enabled, updateScreen=True):
  1114.     """Sets the color inversion.
  1115.  
  1116.     Arguments:
  1117.     - enabled: Whether or not color inversion should be enabled
  1118.     - updateScreen:   Whether or not to update the screen
  1119.     """
  1120.  
  1121.     if not _initialized:
  1122.         return
  1123.  
  1124.     bonobo.pbclient_set_boolean(_zoomerPBag, "inverse-video", enabled)
  1125.  
  1126.     if updateScreen:
  1127.         _zoomer.markDirty(_roi)
  1128.  
  1129. def setZoomerBrightness(red=0, green=0, blue=0, updateScreen=True):
  1130.     """Increases/Decreases the brightness level by the specified
  1131.     increments.  Increments are floats ranging from -1 (black/no
  1132.     brightenss) to 1 (white/100% brightness).  0 means no change.
  1133.  
  1134.     Arguments:
  1135.     - red:    The amount to alter the red brightness level
  1136.     - green:  The amount to alter the green brightness level
  1137.     - blue:   The amount to alter the blue brightness level
  1138.     - updateScreen:   Whether or not to update the screen
  1139.     """
  1140.  
  1141.     if not _initialized:
  1142.         return
  1143.  
  1144.     _zoomer.setBrightness(red, green, blue)
  1145.  
  1146.     if updateScreen:
  1147.         _zoomer.markDirty(_roi)
  1148.  
  1149. def setZoomerContrast(red=0, green=0, blue=0, updateScreen=True):
  1150.     """Increases/Decreases the contrast level by the specified
  1151.     increments.  Increments are floats ranging from -1 (grey/no
  1152.     contrast) to 1 (white/back/100% contrast).  0 means no change.
  1153.  
  1154.     Arguments:
  1155.     - red:    The amount to alter the red contrast level
  1156.     - green:  The amount to alter the green contrast level
  1157.     - blue:   The amount to alter the blue contrast level
  1158.     - updateScreen:  Whether or not to update the screen
  1159.     """
  1160.  
  1161.     if not _initialized:
  1162.         return
  1163.  
  1164.     _zoomer.setContrast(red, green, blue)
  1165.  
  1166.     if updateScreen:
  1167.         _zoomer.markDirty(_roi)
  1168.  
  1169. def setMagnifierObjectSize(magProperty, size, updateScreen=True):
  1170.     """Sets the specified magnifier property to the specified size.
  1171.  
  1172.     Arguments:
  1173.     - magProperty:   The property to set (as a string)
  1174.     - size:          The size to apply
  1175.     - updateScreen:  Whether or not to update the screen
  1176.     """
  1177.  
  1178.     if not _initialized:
  1179.         return
  1180.  
  1181.     bonobo.pbclient_set_long(_magnifierPBag, magProperty, size)
  1182.  
  1183.     if updateScreen:
  1184.         _zoomer.markDirty(_roi)
  1185.  
  1186. def setZoomerObjectSize(magProperty, size, updateScreen=True):
  1187.     """Sets the specified zoomer property to the specified size.
  1188.  
  1189.     Arguments:
  1190.     - magProperty:   The property to set (as a string)
  1191.     - size:          The size to apply
  1192.     - updateScreen:  Whether or not to update the screen
  1193.     """
  1194.  
  1195.     if not _initialized:
  1196.         return
  1197.  
  1198.     if magProperty == "border-size":
  1199.         try:
  1200.             left = right = top = bottom = 0
  1201.             if orca_state.zoomerType == settings.MAG_ZOOMER_TYPE_RIGHT_HALF:
  1202.                 left = size
  1203.             elif orca_state.zoomerType == settings.MAG_ZOOMER_TYPE_LEFT_HALF:
  1204.                 right = size
  1205.             elif orca_state.zoomerType == settings.MAG_ZOOMER_TYPE_TOP_HALF:
  1206.                 bottom = size
  1207.             elif orca_state.zoomerType == settings.MAG_ZOOMER_TYPE_BOTTOM_HALF:
  1208.                 top = size
  1209.             elif orca_state.zoomerType == settings.MAG_ZOOMER_TYPE_CUSTOM:
  1210.                 if _targetDisplayBounds.x1 > _sourceDisplayBounds.x1:
  1211.                     left = size
  1212.                 if _targetDisplayBounds.x2 < _sourceDisplayBounds.x2:
  1213.                     right = size
  1214.                 if _targetDisplayBounds.y1 > _sourceDisplayBounds.y1:
  1215.                     top = size
  1216.                 if _targetDisplayBounds.y2 < _sourceDisplayBounds.y2:
  1217.                     bottom = size
  1218.                 
  1219.             bonobo.pbclient_set_long(_zoomerPBag, "border-size-left", left)
  1220.             bonobo.pbclient_set_long(_zoomerPBag, "border-size-top", top)
  1221.             bonobo.pbclient_set_long(_zoomerPBag, "border-size-right", right)
  1222.             bonobo.pbclient_set_long(_zoomerPBag, "border-size-bottom", bottom)
  1223.         except:
  1224.             bonobo.pbclient_set_long(_zoomerPBag, "border-size", size)
  1225.     else:
  1226.         bonobo.pbclient_set_long(_zoomerPBag, magProperty, size)
  1227.  
  1228.     if updateScreen:
  1229.         _zoomer.markDirty(_roi)
  1230.  
  1231. def setZoomerObjectColor(magProperty, colorSetting, updateScreen=True):
  1232.     """Sets the specified zoomer property to the specified color.
  1233.  
  1234.     Arguments:
  1235.     - magProperty:  The property to set (as a string)
  1236.     - colorSetting: The Orca color setting to apply
  1237.     - updateScreen:  Whether or not to update the screen
  1238.     """
  1239.  
  1240.     if not _initialized:
  1241.         return
  1242.  
  1243.     colorPreference = gtk.gdk.color_parse(colorSetting)
  1244.  
  1245.     # Convert the colorPreference string to something we can use.
  1246.     # The main issue here is that the color preferences are saved
  1247.     # as 4 byte values per color.  We only need 2 bytes, so we
  1248.     # get rid of the bottom 8 bits.
  1249.     #
  1250.     colorPreference.red   = colorPreference.red   >> 8
  1251.     colorPreference.blue  = colorPreference.blue  >> 8
  1252.     colorPreference.green = colorPreference.green >> 8
  1253.     colorString = "0x%02X%02X%02X" \
  1254.                   % (colorPreference.red,
  1255.                      colorPreference.green,
  1256.                      colorPreference.blue)
  1257.  
  1258.     toChange = _zoomerPBag.getValue(magProperty)
  1259.     _zoomerPBag.setValue(magProperty,
  1260.                          ORBit.CORBA.Any(toChange.typecode(),
  1261.                                          long(colorString, 0)))
  1262.     if updateScreen:
  1263.         _zoomer.markDirty(_roi)
  1264.  
  1265. def setMagnifierObjectColor(magProperty, colorSetting, updateScreen=True):
  1266.     """Sets the specified magnifier property to the specified color.
  1267.  
  1268.     Arguments:
  1269.     - magProperty:  The property to set (as a string)
  1270.     - colorSetting: The Orca color setting to apply
  1271.     - updateScreen:  Whether or not to update the screen
  1272.     """
  1273.  
  1274.     if not _initialized:
  1275.         return
  1276.  
  1277.     colorPreference = gtk.gdk.color_parse(colorSetting)
  1278.  
  1279.     # Convert the colorPreference string to something we can use.
  1280.     # The main issue here is that the color preferences are saved
  1281.     # as 4 byte values per color.  We only need 2 bytes, so we
  1282.     # get rid of the bottom 8 bits.
  1283.     #
  1284.     colorPreference.red   = colorPreference.red   >> 8
  1285.     colorPreference.blue  = colorPreference.blue  >> 8
  1286.     colorPreference.green = colorPreference.green >> 8
  1287.     colorString = "0x%02X%02X%02X" \
  1288.                   % (colorPreference.red,
  1289.                      colorPreference.green,
  1290.                      colorPreference.blue)
  1291.  
  1292.     toChange = _magnifierPBag.getValue(magProperty)
  1293.     _magnifierPBag.setValue(magProperty,
  1294.                             ORBit.CORBA.Any(toChange.typecode(),
  1295.                                             long(colorString, 0)))
  1296.     if updateScreen:
  1297.         _zoomer.markDirty(_roi)
  1298.  
  1299. def setZoomerMagFactor(x, y, updateScreen=True):
  1300.     """Sets the magnification level.
  1301.  
  1302.     Arguments:
  1303.     - x: The horizontal magnification level
  1304.     - y: The vertical magnification level
  1305.     - updateScreen:  Whether or not to update the screen
  1306.     """
  1307.  
  1308.     global _minROIX
  1309.     global _minROIY
  1310.  
  1311.     if not _initialized:
  1312.         return
  1313.  
  1314.     [oldX, oldY] = _zoomer.getMagFactor()
  1315.  
  1316.     _zoomer.setMagFactor(x, y)
  1317.  
  1318.     if updateScreen:
  1319.         __updateROIDimensions()
  1320.         if (oldX > x) and (x < 1.5):
  1321.             _minROIX = _sourceDisplayBounds.x1
  1322.             _minROIY = _sourceDisplayBounds.y1
  1323.             __setROI(GNOME.Magnifier.RectBounds(_minROIX,
  1324.                                                 _minROIY,
  1325.                                                 _minROIX + _roiWidth,
  1326.                                                 _minROIY + _roiHeight))
  1327.         else:
  1328.             extents = orca_state.locusOfFocus.queryComponent().getExtents(0)
  1329.             __setROICenter(extents.x, extents.y)
  1330.  
  1331. def setZoomerSmoothingType(smoothingType, updateScreen=True):
  1332.     """Sets the zoomer's smoothing type.
  1333.  
  1334.     Arguments:
  1335.     - smoothingType: The type of smoothing to use
  1336.     - updateScreen:  Whether or not to update the screen
  1337.     """
  1338.  
  1339.     if not _initialized:
  1340.         return
  1341.  
  1342.     if smoothingType == settings.MAG_SMOOTHING_MODE_BILINEAR:
  1343.         string = "bilinear"
  1344.     else:
  1345.         string = "None"
  1346.  
  1347.     try:
  1348.         bonobo.pbclient_set_string(_zoomerPBag, "smoothing-type", string)
  1349.     except:
  1350.         pass
  1351.  
  1352.     if updateScreen:
  1353.         _zoomer.markDirty(_roi)
  1354.  
  1355. def setZoomerColorFilter(colorFilter, updateScreen=True):
  1356.     """Sets the zoomer's color filter.
  1357.  
  1358.     Arguments:
  1359.     - colorFilter: The color filter to apply
  1360.     - updateScreen:  Whether or not to update the screen
  1361.     """
  1362.  
  1363.     if not _initialized or not isFilteringCapable():
  1364.         return
  1365.  
  1366.     if colorFilter == settings.MAG_COLOR_FILTERING_MODE_SATURATE_RED:
  1367.         toApply = _zoomer.COLORBLIND_FILTER_T_SELECTIVE_SATURATE_RED
  1368.     elif colorFilter == settings.MAG_COLOR_FILTERING_MODE_SATURATE_GREEN:
  1369.         toApply = _zoomer.COLORBLIND_FILTER_T_SELECTIVE_SATURATE_GREEN
  1370.     elif colorFilter == settings.MAG_COLOR_FILTERING_MODE_SATURATE_BLUE:
  1371.         toApply = _zoomer.COLORBLIND_FILTER_T_SELECTIVE_SATURATE_BLUE
  1372.     elif colorFilter == settings.MAG_COLOR_FILTERING_MODE_DESATURATE_RED:
  1373.         toApply = _zoomer.COLORBLIND_FILTER_T_SELECTIVE_DESSATURATE_RED
  1374.     elif colorFilter == settings.MAG_COLOR_FILTERING_MODE_DESATURATE_GREEN:
  1375.         toApply = _zoomer.COLORBLIND_FILTER_T_SELECTIVE_DESSATURATE_GREEN
  1376.     elif colorFilter == settings.MAG_COLOR_FILTERING_MODE_DESATURATE_BLUE:
  1377.         toApply = _zoomer.COLORBLIND_FILTER_T_SELECTIVE_DESSATURATE_BLUE
  1378.     elif colorFilter == settings.MAG_COLOR_FILTERING_MODE_NEGATIVE_HUE_SHIFT:
  1379.         toApply = _zoomer.COLORBLIND_FILTER_T_HUE_SHIFT_NEGATIVE
  1380.     elif colorFilter == settings.MAG_COLOR_FILTERING_MODE_POSITIVE_HUE_SHIFT:
  1381.         toApply = _zoomer.COLORBLIND_FILTER_T_HUE_SHIFT_POSITIVE
  1382.     else:
  1383.         toApply = _zoomer.COLORBLIND_FILTER_T_NO_FILTER
  1384.  
  1385.     colorFilter = _zoomerPBag.getValue("color-blind-filter")
  1386.     _zoomerPBag.setValue(
  1387.          "color-blind-filter",
  1388.          ORBit.CORBA.Any(
  1389.              colorFilter.typecode(),
  1390.              toApply))
  1391.  
  1392.     if updateScreen:
  1393.         _zoomer.markDirty(_roi)
  1394.  
  1395. def hideSystemPointer(hidePointer):
  1396.     """Hide or show the system pointer.
  1397.  
  1398.     Arguments:
  1399.     -hidePointer: If True, hide the system pointer, otherwise show it.
  1400.     """
  1401.  
  1402.     # Depends upon new functionality in gnome-mag, so just catch the
  1403.     # exception if this functionality isn't there.
  1404.     #
  1405.     try:
  1406.         if hidePointer:
  1407.             _magnifier.hideCursor()
  1408.         else:
  1409.             _magnifier.showCursor()
  1410.     except:
  1411.         debug.printException(debug.LEVEL_FINEST)
  1412.  
  1413. def isFullScreenCapable():
  1414.     """Returns True if we are capable of doing full screen (i.e. whether
  1415.     composite is being used.
  1416.     """
  1417.  
  1418.     try:
  1419.         capable = _magnifier.fullScreenCapable()
  1420.     except:
  1421.         capable = False
  1422.  
  1423.     return capable
  1424.  
  1425. def isFilteringCapable():
  1426.     """Returns True if we're able to take advantage of libcolorblind's color
  1427.     filtering.
  1428.     """
  1429.  
  1430.     try:
  1431.         capable = _magnifier.supportColorblindFilters()
  1432.     except:
  1433.         capable = False
  1434.  
  1435.     return capable
  1436.  
  1437. def updateMouseTracking(newMode):
  1438.     """Updates the mouse tracking mode.
  1439.  
  1440.     Arguments:
  1441.     -newMode: The new mode to use.
  1442.     """
  1443.  
  1444.     global _mouseTracking
  1445.     _mouseTracking = newMode
  1446.  
  1447. def updateControlTracking(newMode):
  1448.     """Updates the control tracking mode.
  1449.  
  1450.     Arguments:
  1451.     -newMode: The new mode to use.
  1452.     """
  1453.  
  1454.     global _controlTracking
  1455.     _controlTracking = newMode
  1456.  
  1457. def updateTextTracking(newMode):
  1458.     """Updates the text tracking mode.
  1459.  
  1460.     Arguments:
  1461.     -newMode: The new mode to use.
  1462.     """
  1463.  
  1464.     global _textTracking
  1465.     _textTracking = newMode
  1466.  
  1467. def updateEdgeMargin(amount):
  1468.     """Updates the edge margin
  1469.  
  1470.     Arguments:
  1471.     -amount: The new margin to use, in pixels.
  1472.     """
  1473.  
  1474.     global _edgeMargin
  1475.     _edgeMargin = amount
  1476.  
  1477. def updatePointerFollowsFocus(enabled):
  1478.     """Updates the pointer follows focus setting.
  1479.  
  1480.     Arguments:
  1481.     -enabled: whether or not pointer follows focus should be enabled.
  1482.     """
  1483.  
  1484.     global _pointerFollowsFocus
  1485.     _pointerFollowsFocus = enabled
  1486.  
  1487. def updatePointerFollowsZoomer(enabled):
  1488.     """Updates the pointer follows zoomer setting.
  1489.  
  1490.     Arguments:
  1491.     -enabled: whether or not pointer follows zoomer should be enabled.
  1492.     """
  1493.  
  1494.     global _pointerFollowsZoomer
  1495.     _pointerFollowsZoomer = enabled
  1496.  
  1497. def finishLiveUpdating():
  1498.     """Restores things that were altered via a live update."""
  1499.  
  1500.     global _liveUpdatingMagnifier
  1501.     global _mouseTracking
  1502.     global _controlTracking
  1503.     global _textTracking
  1504.     global _edgeMargin
  1505.     global _pointerFollowsFocus
  1506.     global _pointerFollowsZoomer
  1507.  
  1508.     _liveUpdatingMagnifier = False
  1509.     _mouseTracking = settings.magMouseTrackingMode
  1510.     _controlTracking = settings.magControlTrackingMode
  1511.     _textTracking = settings.magTextTrackingMode
  1512.     _edgeMargin = settings.magEdgeMargin
  1513.     _pointerFollowsFocus = settings.magPointerFollowsFocus
  1514.     _pointerFollowsZoomer = settings.magPointerFollowsZoomer
  1515.  
  1516.     if settings.enableMagnifier:
  1517.         setupMagnifier(settings.magZoomerType)
  1518.         init()
  1519.     else:
  1520.         shutdown()
  1521.  
  1522. ######################################################################
  1523. #                                                                    #
  1524. #                        Input Event Handlers                        #
  1525. #                                                                    #
  1526. ######################################################################
  1527.  
  1528. def toggleColorEnhancements(script=None, inputEvent=None):
  1529.     """Toggles the color enhancements on/off."""
  1530.  
  1531.     if not _initialized:
  1532.         return
  1533.  
  1534.     # We don't want to stomp on a command-altered magnification level.
  1535.     #
  1536.     [levelX, levelY] = _zoomer.getMagFactor()
  1537.     
  1538.     normal = {'enableMagZoomerColorInversion': False,
  1539.               'magBrightnessLevelRed': 0,
  1540.               'magBrightnessLevelGreen': 0,
  1541.               'magBrightnessLevelBlue': 0,
  1542.               'magContrastLevelRed': 0,
  1543.               'magContrastLevelGreen': 0,
  1544.               'magContrastLevelBlue': 0,
  1545.               'magColorFilteringMode': settings.MAG_COLOR_FILTERING_MODE_NONE,
  1546.               'magSmoothingMode': settings.MAG_SMOOTHING_MODE_BILINEAR,
  1547.               'magZoomerBorderColor': '#000000',
  1548.               'magZoomFactor': levelX}
  1549.  
  1550.     if orca_state.colorEnhancementsEnabled:
  1551.         __setupZoomer(restore = normal)
  1552.         # Translators: "color enhancements" are changes users can
  1553.         # make to the appearance of the screen to make things easier
  1554.         # to see, such as inverting the colors or applying a tint.
  1555.         #
  1556.         speech.speak(_("Color enhancements disabled."))
  1557.     else:
  1558.         toRestore = {'magZoomFactor': levelX}
  1559.         __setupZoomer(restore = toRestore)
  1560.         # Translators: "color enhancements" are changes users can
  1561.         # make to the appearance of the screen to make things easier
  1562.         # to see, such as inverting the colors or applying a tint.
  1563.         #
  1564.         speech.speak(_("Color enhancements enabled."))
  1565.  
  1566.     orca_state.colorEnhancementsEnabled = \
  1567.                                     not orca_state.colorEnhancementsEnabled
  1568.  
  1569.     return True
  1570.  
  1571. def toggleMouseEnhancements(script=None, inputEvent=None):
  1572.     """Toggles the mouse enhancements on/off."""
  1573.  
  1574.     if not _initialized:
  1575.         return
  1576.  
  1577.     if orca_state.mouseEnhancementsEnabled:
  1578.         setMagnifierCrossHair(False, False)
  1579.         setMagnifierObjectColor("cursor-color", "#000000", False)
  1580.         setMagnifierCursor(True, False, 0)
  1581.         # Translators: "mouse enhancements" are changes users can
  1582.         # make to the appearance of the mouse pointer to make it
  1583.         # easier to see, such as increasing its size, changing its
  1584.         # color, and surrounding it with crosshairs.
  1585.         #
  1586.         speech.speak(_("Mouse enhancements disabled."))
  1587.     else:
  1588.         # We normally toggle "on" what the user has enabled by default.
  1589.         # However, if the user's default settings are to disable all mouse
  1590.         # enhancements "on" and "off" are the same thing.  If that is the
  1591.         # case and this command is being used, the user probably expects
  1592.         # to see *something* change. We don't know what, so enable both
  1593.         # the cursor and the cross-hairs.
  1594.         #
  1595.         cursorEnable = settings.enableMagCursor
  1596.         crossHairEnable = settings.enableMagCrossHair
  1597.         if not (cursorEnable or crossHairEnable):
  1598.             cursorEnable = True
  1599.             crossHairEnable = True
  1600.  
  1601.         setMagnifierCursor(cursorEnable,
  1602.                            settings.enableMagCursorExplicitSize,
  1603.                            settings.magCursorSize,
  1604.                            False)
  1605.         setMagnifierObjectColor("cursor-color",
  1606.                                 settings.magCursorColor,
  1607.                                 False)
  1608.         setMagnifierObjectColor("crosswire-color",
  1609.                                 settings.magCrossHairColor,
  1610.                                 False)
  1611.         setMagnifierCrossHairClip(settings.enableMagCrossHairClip,
  1612.                                   False)
  1613.         setMagnifierCrossHair(crossHairEnable)
  1614.         # Translators: "mouse enhancements" are changes users can
  1615.         # make to the appearance of the mouse pointer to make it
  1616.         # easier to see, such as increasing its size, changing its
  1617.         # color, and surrounding it with crosshairs.
  1618.         #
  1619.         speech.speak(_("Mouse enhancements enabled."))
  1620.  
  1621.     orca_state.mouseEnhancementsEnabled = \
  1622.                                     not orca_state.mouseEnhancementsEnabled
  1623.     return True
  1624.  
  1625. def increaseMagnification(script=None, inputEvent=None):
  1626.     """Increases the magnification level."""
  1627.  
  1628.     if not _initialized:
  1629.         return
  1630.  
  1631.     [levelX, levelY] = _zoomer.getMagFactor()
  1632.  
  1633.     # Move in increments that are sensible based on the current level of
  1634.     # magnification.
  1635.     #
  1636.     if 1 <= levelX < 4:
  1637.         increment = 0.25
  1638.     elif 4 <= levelX < 7:
  1639.         increment = 0.5
  1640.     else:
  1641.         increment = 1
  1642.  
  1643.     newLevel = levelX + increment
  1644.     if newLevel <= 16:
  1645.         setZoomerMagFactor(newLevel, newLevel)
  1646.         speech.speak(str(newLevel))
  1647.  
  1648.     return True
  1649.  
  1650. def decreaseMagnification(script=None, inputEvent=None):
  1651.     """Decreases the magnification level."""
  1652.  
  1653.     if not _initialized:
  1654.         return
  1655.  
  1656.     [levelX, levelY] = _zoomer.getMagFactor()
  1657.  
  1658.     # Move in increments that are sensible based on the current level of
  1659.     # magnification.
  1660.     #
  1661.     if 1 <= levelX < 4:
  1662.         increment = 0.25
  1663.     elif 4 <= levelX < 7:
  1664.         increment = 0.5
  1665.     else:
  1666.         increment = 1
  1667.  
  1668.     newLevel = levelX - increment
  1669.     if newLevel >= 1:
  1670.         setZoomerMagFactor(newLevel, newLevel)
  1671.         speech.speak(str(newLevel))
  1672.  
  1673.     return True
  1674.  
  1675. def toggleMagnifier(script=None, inputEvent=None):
  1676.     """Toggles the magnifier."""
  1677.  
  1678.     if not _initialized:
  1679.         init()
  1680.         # Translators: this is the message spoken when a user enables the
  1681.         # magnifier.  In addition to screen magnification, the user's
  1682.         # preferred colors and mouse customizations are loaded.
  1683.         #
  1684.         speech.speak(_("Magnifier enabled."))
  1685.     else:
  1686.         shutdown()
  1687.         # Translators: this is the message spoken when a user disables the
  1688.         # magnifier, restoring the screen contents to their normal colors
  1689.         # and sizes.
  1690.         #
  1691.         speech.speak(_("Magnifier disabled."))
  1692.  
  1693.     return True
  1694.  
  1695. def cycleZoomerType(script=None, inputEvent=None):
  1696.     """Allows the user to cycle through the available zoomer types."""
  1697.  
  1698.     if not _initialized:
  1699.         return
  1700.  
  1701.     # There are 6 possible zoomer types
  1702.     #
  1703.     orca_state.zoomerType += 1
  1704.  
  1705.     if orca_state.zoomerType >= 6:
  1706.         orca_state.zoomerType = 0
  1707.  
  1708.     if orca_state.zoomerType == settings.MAG_ZOOMER_TYPE_FULL_SCREEN \
  1709.        and not _fullScreenCapable:
  1710.         orca_state.zoomerType += 1
  1711.  
  1712.     # We don't want to stomp on any command-altered settings
  1713.     #
  1714.     toRestore = {}
  1715.  
  1716.     [levelX, levelY] = _zoomer.getMagFactor()
  1717.     if levelX != settings.magZoomFactor:
  1718.         toRestore['magZoomFactor'] = levelX
  1719.  
  1720.     if not orca_state.colorEnhancementsEnabled:
  1721.         toRestore.update(\
  1722.             {'enableMagZoomerColorInversion': False,
  1723.              'magBrightnessLevelRed': 0,
  1724.              'magBrightnessLevelGreen': 0,
  1725.              'magBrightnessLevelBlue': 0,
  1726.              'magContrastLevelRed': 0,
  1727.              'magContrastLevelGreen': 0,
  1728.              'magContrastLevelBlue': 0,
  1729.              'magColorFilteringMode': settings.MAG_COLOR_FILTERING_MODE_NONE,
  1730.              'magSmoothingMode': settings.MAG_SMOOTHING_MODE_BILINEAR,
  1731.              'magZoomerBorderColor': '#000000'})
  1732.  
  1733.     setupMagnifier(orca_state.zoomerType, restore = toRestore)
  1734.  
  1735.     if not orca_state.mouseEnhancementsEnabled:
  1736.         setMagnifierCrossHair(False)
  1737.         setMagnifierObjectColor("cursor-color",
  1738.                                 settings.magCursorColor,
  1739.                                 False)
  1740.         setMagnifierCursor(False, False, 0)
  1741.  
  1742.     if orca_state.zoomerType == settings.MAG_ZOOMER_TYPE_FULL_SCREEN:
  1743.         if _fullScreenCapable:
  1744.             # Translators: magnification will use the full screen.
  1745.             #
  1746.             zoomerType = _("Full Screen")
  1747.         else:
  1748.             # Translators: the user attempted to switch to full screen
  1749.             # magnification, but his/her system doesn't support it.
  1750.             #
  1751.             zoomerType = _("Full Screen mode unavailable")
  1752.     elif orca_state.zoomerType == settings.MAG_ZOOMER_TYPE_TOP_HALF:
  1753.         # Translators: magnification will use the top half of the screen.
  1754.         #
  1755.         zoomerType = _("Top Half")
  1756.     elif orca_state.zoomerType == settings.MAG_ZOOMER_TYPE_BOTTOM_HALF:
  1757.         # Translators: magnification will use the bottom half of the screen.
  1758.         #
  1759.         zoomerType = _("Bottom Half")
  1760.     elif orca_state.zoomerType == settings.MAG_ZOOMER_TYPE_LEFT_HALF:
  1761.         # Translators: magnification will use the left half of the screen.
  1762.         #
  1763.         zoomerType = _("Left Half")
  1764.     elif orca_state.zoomerType == settings.MAG_ZOOMER_TYPE_RIGHT_HALF:
  1765.         # Translators: magnification will use the right half of the screen.
  1766.         #
  1767.         zoomerType = _("Right Half")
  1768.     elif orca_state.zoomerType == settings.MAG_ZOOMER_TYPE_CUSTOM:
  1769.         # Translators: the user has selected a custom area of the screen
  1770.         # to use for magnification.
  1771.         #
  1772.         zoomerType = _("Custom")
  1773.     else:
  1774.         # This shouldn't happen, but just in case....
  1775.         zoomerType = ""
  1776.  
  1777.     speech.speak(zoomerType)
  1778.  
  1779.     return True
  1780.